#include "nvr.h"
#include <QDir>
#include <QFile>
#include <QImage>
#include "commonfuc.h"
#include "logfile.h"

#define JPGBUFFSIZE 1920*540*3//1920*1080*3/2

void CALLBACK fNVRLoginResultCB(LONG lUserID, DWORD dwResult, LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo , void* pUser)
{
    NVR *tmp = (NVR*)pUser;

    int startDChan,iPChanNum;
    if (dwResult == 1)
    {
        startDChan=lpDeviceInfo->byStartDChan;
        iPChanNum =lpDeviceInfo->byIPChanNum+(lpDeviceInfo->byHighDChanNum<<8);
    }
    emit tmp->loginOver(lUserID, dwResult,startDChan,iPChanNum);
}

NVR::NVR(STRUCT_NVR_INFO &nvrInfo) : m_lUserID(-1),m_nvrInfo(nvrInfo),m_devState(false),m_cfg(),m_startDChan(-1),
    m_IPChanNum(0),m_openList(),m_eventLoop(Q_NULLPTR)
{
    connect(this, SIGNAL(loginOver(long,long,int,int)), this, SLOT(onLoginOver(long,long,int,int)));
    connect(this, SIGNAL(sigSnapAndGetVideo(QByteArray,int,LONG)), this, SLOT(onSnapAndGetVideo(QByteArray,int,LONG)));

    this->start();
}

NVR::~NVR()
{
    if (m_eventLoop != Q_NULLPTR)
    {
        m_eventLoop->exit();
    }
    devAntiInit();
    m_openList.clear();

    this->quit();
    this->wait();
}

void NVR::run()
{
    QTimer timer;
    m_eventLoop = new QEventLoop();
    connect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
    timer.start(1000);
    m_eventLoop->exec();
    delete m_eventLoop;
    m_eventLoop = Q_NULLPTR;
}

void NVR::devInit()
{
    DEBUG_PRINT << m_nvrInfo.ip << " login";
    QString strLog;
    strLog.clear();
    strLog.sprintf("%s login",m_nvrInfo.ip.toStdString().c_str());
    logFile::printLog(strLog);
    // 注册IPC设备，异步登录
    NET_DVR_USER_LOGIN_INFO loginInfo;
    memset(&loginInfo, 0, sizeof(loginInfo));
    strcpy(loginInfo.sDeviceAddress, m_nvrInfo.ip.toLatin1().data());
    loginInfo.wPort = m_nvrInfo.port;
    strcpy(loginInfo.sUserName, m_nvrInfo.username.toLatin1().data());
    strcpy(loginInfo.sPassword, m_nvrInfo.password.toLatin1().data());
    loginInfo.cbLoginResult = fNVRLoginResultCB;
    loginInfo.pUser = (void *)this;
    loginInfo.bUseAsynLogin = true;

    NET_DVR_DEVICEINFO_V40 struDeviceInfoV40;
    memset(&struDeviceInfoV40, 0, sizeof(struDeviceInfoV40));

    NET_DVR_Login_V40(&loginInfo, &struDeviceInfoV40);
}

void NVR::devAntiInit()
{
    DEBUG_PRINT << m_nvrInfo.ip << " logout";
    //注销设备
    if (m_lUserID >= 0)
    {
        if (!NET_DVR_Logout(m_lUserID))
        {
            WARNING_PRINT << m_nvrInfo.ip << "NET_DVR_Logout error:" << NET_DVR_GetLastError();
        }
        else
        {
            m_lUserID = -1;
        }
    }
}

void NVR::onLoginOver(long lUserID, long dwResult, int startDChan,int iPChanNum)
{
    QString strLog;
    if (dwResult == 1)
    {
        m_lUserID = lUserID;
        m_devState = true;

        m_startDChan=startDChan;
        m_IPChanNum=iPChanNum;
        DEBUG_PRINT << m_nvrInfo.ip << "success" << " m_startDChan=" << m_startDChan << " m_IPChanNum=" << m_IPChanNum << " m_lUserID=" << m_lUserID;
        strLog.clear();
        strLog.sprintf("%s login success,m_startDChan=%d,m_IPChanNum=%d,m_lUserID=%ld",m_nvrInfo.ip.toStdString().c_str(),m_startDChan,m_IPChanNum,m_lUserID);
        logFile::printLog(strLog);
    }
    else
    {
        m_lUserID = -1;
        int err=NET_DVR_GetLastError();
        ERROR_PRINT << m_nvrInfo.ip << " NET_DVR_Login_V40 error:" << err;
        strLog.clear();
        strLog.sprintf("%s NET_DVR_Login_V40 error:%d",m_nvrInfo.ip.toStdString().c_str(),err);
        logFile::printLog(strLog);
        if(1==err)//用户名或者密码错误，不要再登录
        {
            ERROR_PRINT << m_nvrInfo.ip << " NET_DVR_Login_V40 username or password error";
            strLog.clear();
            strLog.sprintf("%s NET_DVR_Login_V40 username or password error",m_nvrInfo.ip.toStdString().c_str());
            logFile::printLog(strLog);
            m_lUserID = -2;
        }
    }
}

void NVR::onTimeout()
{
    if (m_lUserID == -1)
    {
        m_lUserID = -2;
        this->devInit();
    }
    downLoadVideo();
}

void NVR::onSnapAndGetVideo(QByteArray data, int channel, LONG lRealPlayHandle)
{
    QString strLog;
    DEBUG_PRINT << m_nvrInfo.ip << " channel:" << channel << " opendoor come in";
    strLog.clear();
    strLog.sprintf("%s channel:%d opendoor come in",m_nvrInfo.ip.toStdString().c_str(),channel);
    logFile::printLog(strLog);
    if(m_openList.size()>1000)
    {
        WARNING_PRINT << "too many SnapAndGetVideo had not deal!";
        strLog.clear();
        strLog.sprintf("too many SnapAndGetVideo had not deal!");
        logFile::printLog(strLog);
        emit snapAndGetVideoOK(data,NULL,NULL,NULL,NULL);
        return;
    }
    if(channel>m_IPChanNum-1)//通道偏置大于最大通道数
    {
        WARNING_PRINT << m_nvrInfo.ip << " channel:" << channel << " large than IPChanNum,IPChanNum=" << m_IPChanNum;
        strLog.clear();
        strLog.sprintf("%s channel:%d large than IPChanNum,IPChanNum=%d",m_nvrInfo.ip.toStdString().c_str(),channel,m_IPChanNum);
        logFile::printLog(strLog);
        emit snapAndGetVideoOK(data,NULL,NULL,NULL,NULL);
        return;
    }

    QString dateTimeStr;
    QString strRootPath = CommonFuc::getAppDirPath();
    dateTimeStr.append(strRootPath);
    dateTimeStr.append("record/");
    QDateTime dateTime=QDateTime::currentDateTime();
    dateTimeStr.append(dateTime.toString("yyyy-MM-dd_hh_mm_ss_zzz"));
    dateTimeStr.append(".mp4");
    STRUCT_SNAPANDVIDEO snapAndVideo;
    snapAndVideo.lRealPlayHandle=lRealPlayHandle;
    snapAndVideo.channel=channel;
    snapAndVideo.data=data;
    snapAndVideo.stime=time(NULL);
    snapAndVideo.nvrTime=getNVRTime();
    if(snapAndVideo.nvrTime<=0)
        snapAndVideo.nvrTime=snapAndVideo.stime;
    else
        snapAndVideo.getNVRTimeOK=true;
    snapAndVideo.filePath.append(dateTimeStr);

    logFile::printLog(dateTimeStr);
    qDebug()<<"zzzzzzzzz--------------"<<dateTimeStr;

	memset(snapAndVideo.jpg1,0,sizeof(snapAndVideo.jpg1));
	memset(snapAndVideo.jpg2,0,sizeof(snapAndVideo.jpg2));
    memset(snapAndVideo.jpg3,0,sizeof(snapAndVideo.jpg3));

    m_openList.append(snapAndVideo);
}

int NVR::downLoadVideo()
{
    QString strLog;
    time_t currTime=time(NULL);
    int diffTime;
    time_t endT;
    int i;
    int index;
    long err;

    if(m_openList.size()<=0)
        return 0;

    for(i=0;i<m_openList.size();)
    {
    diffTime=currTime-m_openList[i].stime;
    index=0;
    if(3==m_openList[i].jpgStep)//如果已经抓拍了三张，则继续下一个
        goto GETVIDEO;
    if(m_lUserID>=0&&(m_openList[i].channel<m_IPChanNum)&&(m_startDChan>=0))
    {       
        switch (m_openList[i].jpgStep) {
        case 0:
            index=1;
            break;
        case 1:
            if(diffTime>=2)
                index=2;
//            else
//                index=-1;//list按时间排序，所以这次该列表时间没到下面的列表就不用看了,取图片间隔为1s的时候用
            break;
        case 2:
            if(diffTime>=4)
                index=3;
            break;
        default:
            index=0;
            break;
        }

        if(-1==index)//推出for循环
            return 0;
        if(0!=index)//还要抓拍
        {
            QByteArray jpg;
            DWORD buffLen;
            bool ret=false;
            jpg.resize(JPGBUFFSIZE);

#if(0)
            #ifdef WIN32
                ret=PlayM4_GetJPEG(NET_DVR_GetRealPlayerIndex(lRealPlayHandle), (unsigned char*)jpg.data(), jpg.size(), &buffLen);
            #else
                ret=PlayM4_GetJPEG(NET_DVR_GetRealPlayerIndex(lRealPlayHandle), (unsigned char*)jpg.data(), jpg.size(), &buffLen);
            #endif
#else
            NET_DVR_JPEGPARA jpegPara;
            jpegPara.wPicSize=0xff;
            jpegPara.wPicQuality=1;
            ret=NET_DVR_CaptureJPEGPicture_NEW(m_lUserID,m_openList[i].channel+m_startDChan,&jpegPara,jpg.data(),jpg.size(),&buffLen);
#endif
            if(ret&&(buffLen > 0) &&((int)buffLen < jpg.size()))
            {
                QString temp;

                QString strRoot = CommonFuc::getAppDirPath();
                temp.append(strRoot);
                temp.append("record/");
                QDir nvrDir;
                if (!nvrDir.exists(temp))
                {
                    nvrDir.mkdir(temp);
                }

                temp.append(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh_mm_ss_zzz_"));
                temp.append(QString::number(index));//test
                temp.append(".jpg");
                QImage img;
                img.loadFromData((unsigned char*)jpg.data(), buffLen);
//                   saveFile(temp,jpg);
                img.save(temp,"JPG");

                switch (index) {
                case 1:
                    strcpy(m_openList[i].jpg1,temp.toStdString().data());
                    break;
                case 2:
                    strcpy(m_openList[i].jpg2,temp.toStdString().data());
                    break;
                case 3:
                    strcpy(m_openList[i].jpg3,temp.toStdString().data());
                    break;
                default:
                    break;
                }
                m_openList[i].jpgStep=index;
            }
        }
    }
    else
    {
        WARNING_PRINT << m_nvrInfo.ip << " channel:" << m_openList[i].channel << " get pic fail";
        strLog.clear();
        strLog.sprintf("%s channel:%d get pic fail",m_nvrInfo.ip.toStdString().c_str(),m_openList[i].channel);
        logFile::printLog(strLog);
        m_openList[i].jpgStep=3;
    }
GETVIDEO:
    endT=m_openList[i].stime+m_cfg.m_cfg.general.saveVideoTBack;
    if(currTime>endT+180)// 大于结束时间3min
    {
        WARNING_PRINT << m_nvrInfo.ip << " channel:" << m_openList[i].channel << " get pic and video outtime";
        strLog.clear();
        strLog.sprintf("%s channel:%d get pic and video outtime",m_nvrInfo.ip.toStdString().c_str(),m_openList[i].channel);
        logFile::printLog(strLog);
        m_openList[i].jpgStep=3;
        m_openList[i].videoStep=2;
        goto GETVIDEOOK;
    }
    if((0==m_openList[i].videoStep)&&(false==m_openList[i].getNVRTimeOK))//增加取nvr时间ro冗余，未获取nvr时间成功并且未开始取视频
    {
        time_t pcTTmp=time(NULL);
        time_t nvrTTmp=getNVRTime();
        time_t tTmp=0;
        if(nvrTTmp>0)//取时间成功
        {
            tTmp=m_openList[i].nvrTime+nvrTTmp-pcTTmp;
        }
        if(tTmp>0)
        {
            m_openList[i].nvrTime=tTmp;
            m_openList[i].getNVRTimeOK=true;
        }
    }
    if((0==m_openList[i].videoStep)&&(currTime>endT))//为开始下载并且时间差大于后几秒,则下载视频
    {
        NET_DVR_PLAYCOND struDownloadCond={0};
        struct tm* localTimeTmp;
        time_t ttmp;

        if((m_openList[i].channel>=m_IPChanNum)||(m_startDChan<0)||(m_lUserID<0))
        {
            WARNING_PRINT << m_nvrInfo.ip << " channel:" << m_openList[i].channel << "  get video fail";
            strLog.clear();
            strLog.sprintf("%s channel:%d get video fail",m_nvrInfo.ip.toStdString().c_str(),m_openList[i].channel);
            logFile::printLog(strLog);
            m_openList[i].videoStep=2;
            goto GETVIDEOOK;
//            return -1;
        }

		m_openList[i].videoUpdateT++;
        if(0==m_openList[i].videoUpdateSta)//未更新成功
        {
            if(!NET_DVR_UpdateRecordIndex(m_lUserID,m_openList[i].channel+m_startDChan))
            {
                err=NET_DVR_GetLastError();
                WARNING_PRINT << m_nvrInfo.ip << " channel:" << m_openList[i].channel << " NET_DVR_UpdateRecordIndex error:" << err;
                strLog.clear();
                strLog.sprintf("%s channel:%d NET_DVR_UpdateRecordIndex error:%ld",m_nvrInfo.ip.toStdString().c_str(),m_openList[i].channel,err);
                logFile::printLog(strLog);
//                if(m_openList[i].videoUpdateT>=3)//3次机会
//                {
//                    m_openList[i].videoStep=2;
//                    goto GETVIDEOOK;
//                }
            }
            else
            {
                m_openList[i].videoUpdateSta=1;
//                DEBUG_PRINT << m_nvrInfo.ip << " channel:" << m_openList[i].channel << " NET_DVR_UpdateRecordIndex ok";
//                strLog.sprintf("%s channel:%d NET_DVR_UpdateRecordIndex ok",m_nvrInfo.ip.toStdString().c_str(),m_openList[i].channel);
//                logFile::printLog(strLog);
            }
        }
		
        if(m_openList[i].videoUpdateT<=3)//更新后要大于等于3个定时器周期才取视频
        {
            goto GETVIDEOOK;
        }

//##################################下载文件########################################//
        ttmp=m_openList[i].nvrTime-m_cfg.m_cfg.general.saveVideoTForward;
        localTimeTmp=localtime(&ttmp);
        struDownloadCond.dwChannel=m_openList[i].channel+m_startDChan; //通道号
        struDownloadCond.struStartTime.dwYear = 1900+localTimeTmp->tm_year; //开始时间
        struDownloadCond.struStartTime.dwMonth = localTimeTmp->tm_mon+1;
        struDownloadCond.struStartTime.dwDay = localTimeTmp->tm_mday;
        struDownloadCond.struStartTime.dwHour = localTimeTmp->tm_hour;
        struDownloadCond.struStartTime.dwMinute = localTimeTmp->tm_min;
        struDownloadCond.struStartTime.dwSecond =localTimeTmp->tm_sec;
        ttmp=m_openList[i].nvrTime+m_cfg.m_cfg.general.saveVideoTBack;
        localTimeTmp=localtime(&ttmp);
        struDownloadCond.struStopTime.dwYear = 1900+localTimeTmp->tm_year; //结束时间
        struDownloadCond.struStopTime.dwMonth = localTimeTmp->tm_mon+1;
        struDownloadCond.struStopTime.dwDay = localTimeTmp->tm_mday;
        struDownloadCond.struStopTime.dwHour = localTimeTmp->tm_hour;
        struDownloadCond.struStopTime.dwMinute = localTimeTmp->tm_min;
        struDownloadCond.struStopTime.dwSecond = localTimeTmp->tm_sec;
        //---------------------------------------
        //按时间下载
        if(m_openList[i].filePath.isEmpty() || m_openList[i].filePath.isNull())
        {
            QString str = "m_openList[i].filePath is Empty!";
            logFile::printLog(str);
            goto GETVIDEOOK;
        }
        m_openList[i].hPlayback = NET_DVR_GetFileByTime_V40(m_lUserID, (char *)m_openList[i].filePath.toStdString().c_str(),&struDownloadCond);
        if(m_openList[i].hPlayback < 0)
        {
            err=NET_DVR_GetLastError();
            WARNING_PRINT << m_nvrInfo.ip << " channel:" << m_openList[i].channel << " NET_DVR_GetFileByTime_V40 error:" << err;
            strLog.clear();
            strLog.sprintf("%s channel:%d NET_DVR_GetFileByTime_V40 error:%ld",m_nvrInfo.ip.toStdString().c_str(),m_openList[i].channel,err);
			logFile::printLog(strLog);
            m_openList[i].videoStep=2;
            goto GETVIDEOOK;
        }
        //---------------------------------------
        //开始下载
        if(!NET_DVR_PlayBackControl_V40(m_openList[i].hPlayback, NET_DVR_PLAYSTART, NULL, 0, NULL,NULL))
        {
            err=NET_DVR_GetLastError();
            WARNING_PRINT << m_nvrInfo.ip << " channel:" << m_openList[i].channel << " NET_DVR_PlayBackControl_V40 error:" << err;
            strLog.clear();
            strLog.sprintf("%s channel:%d NET_DVR_PlayBackControl_V40 error:%ld",m_nvrInfo.ip.toStdString().c_str(),m_openList[i].channel,err);
			logFile::printLog(strLog);
            m_openList[i].videoStep=2;
            goto GETVIDEOOK;
        }

        m_openList[i].videoStep=1;
    }
    else if(1==m_openList[i].videoStep)//下载中则读取下载进度
    {
        m_openList[i].nPos = NET_DVR_GetDownloadPos(m_openList[i].hPlayback);
//        DEBUG_PRINT << "i=" << i << " nPos=" << m_openList[i].nPos;
        if((m_openList[i].nPos>=100)||(m_openList[i].nPos<0))
        {
            m_openList[i].videoStep=2;
        }
        if(m_openList[i].nPos<0)
        {
            err=NET_DVR_GetLastError();
            WARNING_PRINT << m_nvrInfo.ip << " channel:" << m_openList[i].channel << " NET_DVR_GetDownloadPos error:" << err;
            strLog.clear();
            strLog.sprintf("%s channel:%d NET_DVR_GetDownloadPos error:%ld",m_nvrInfo.ip.toStdString().c_str(),m_openList[i].channel,err);
			logFile::printLog(strLog);
        }
    }
GETVIDEOOK:
   if(2==m_openList[i].videoStep)//视频录制完成
   {
       if(m_openList[i].hPlayback >= 0)
       {
           NET_DVR_StopGetFile(m_openList[i].hPlayback);//停止下载文件
           m_openList[i].hPlayback=-1;
       }
   }
   if((2==m_openList[i].videoStep)&&(3==m_openList[i].jpgStep)&&(!m_openList[i].hadSend))//已录制完毕但未发送
   {
       QString jpg1,jpg2,jpg3;
       jpg1.append(m_openList[i].jpg1);
       jpg2.append(m_openList[i].jpg2);
       jpg3.append(m_openList[i].jpg3);
       emit snapAndGetVideoOK(m_openList[i].data, jpg1, jpg2, jpg3, m_openList[i].filePath);
	   m_openList[i].hadSend=true;
       DEBUG_PRINT << m_nvrInfo.ip << m_openList[i].channel << " jpg1=" << jpg1 <<  " jpg2=" << jpg2 <<" jpg3=" << jpg3 <<" filePath=" << m_openList[i].filePath;
       strLog.clear();
       strLog.sprintf("%s channel:%d jpg1=%s,jpg2=%s,jpg3=%s,filePath=%s",m_nvrInfo.ip.toStdString().c_str(),m_openList[i].channel,jpg1.toStdString().c_str(),jpg2.toStdString().c_str(),jpg3.toStdString().c_str(),m_openList[i].filePath.toStdString().c_str());
	   logFile::printLog(strLog);
       m_openList.removeAt(i);
   }
   else
   {
       i++;
   }
   }
//   DEBUG_PRINT << "downLoadVideo time=" <<time(NULL)-currTime << " m_openList.size=" << m_openList.size() << " i=" <<i;
   return 0;
}

int NVR::saveFile(QString fileName,QByteArray jpg)
{
    QFile file;
    file.setFileName(fileName);
    if (file.open(QIODevice::WriteOnly ))
    {
        file.write(jpg);
        file.flush();
        file.close();
    }
    return 0;
}

time_t NVR::getNVRTime()
{
    QString strLog;
    DWORD dwReturnLen;
    NET_DVR_TIME dvrTime;
    time_t ttmp=0;
    if(m_lUserID>=0)
    {
        BOOL ret=NET_DVR_GetDVRConfig(m_lUserID,NET_DVR_GET_TIMECFG,0,&dvrTime,sizeof(NET_DVR_TIME),&dwReturnLen);
        if(ret&&(24==dwReturnLen))
        {
            struct tm t={0};
            t.tm_year=dvrTime.dwYear-1900;
            t.tm_mon=dvrTime.dwMonth-1;
            t.tm_mday=dvrTime.dwDay;
            t.tm_hour=dvrTime.dwHour;
            t.tm_min=dvrTime.dwMinute;
            t.tm_sec=dvrTime.dwSecond;
            ttmp=mktime(&t);
//            struct tm *tmtmp=localtime(&ttmp);
//            DEBUG_PRINT << "tmtmp=" << tmtmp->tm_year+1900 << "-" << tmtmp->tm_mon+1 << "-" << tmtmp->tm_mday << " " << tmtmp->tm_hour << ":" << tmtmp->tm_min << ":" << tmtmp->tm_sec;
        }
        else
        {
            long err=NET_DVR_GetLastError();
            WARNING_PRINT << m_nvrInfo.ip << " NET_DVR_GetDVRConfig error:" << err;
            strLog.clear();
            strLog.sprintf("%s NET_DVR_GetDVRConfig error:%ld",m_nvrInfo.ip.toStdString().c_str(),err);
            logFile::printLog(strLog);
        }
    }
    return ttmp;
}
